package org.ehe.finance.voucher.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ehe.common.core.exception.ServiceException;
import org.ehe.common.mybatis.core.page.PageQuery;
import org.ehe.common.mybatis.core.page.TableDataInfo;
import org.ehe.common.satoken.utils.LoginHelper;
import org.ehe.finance.voucher.domain.ErpFinanceVoucher;
import org.ehe.finance.voucher.domain.ErpFinanceVoucherType;
import org.ehe.finance.voucher.domain.bo.*;
import org.ehe.finance.voucher.domain.vo.ErpFinanceVoucherDetailVo;
import org.ehe.finance.voucher.domain.vo.ErpFinanceVoucherTypeVo;
import org.ehe.finance.voucher.domain.vo.ErpFinanceVoucherVo;
import org.ehe.finance.voucher.mapper.ErpFinanceVoucherMapper;
import org.ehe.finance.voucher.mapper.ErpFinanceVoucherMapper.VoucherStatusCount;
import org.ehe.finance.voucher.service.ErpFinanceVoucherDetailService;
import org.ehe.finance.voucher.service.ErpFinanceVoucherService;
import org.ehe.finance.voucher.service.ErpFinanceVoucherTypeService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;

/**
 * 记账凭证服务实现类
 * @author :zhangnn
 * @className :ErpFinanceVoucherServiceImpl
 * @description: TODO
 * @date 2025-08-25 09:36:40
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ErpFinanceVoucherServiceImpl extends ServiceImpl<ErpFinanceVoucherMapper, ErpFinanceVoucher>
    implements ErpFinanceVoucherService {

    private final ErpFinanceVoucherMapper voucherMapper;
    private final ErpFinanceVoucherTypeService voucherTypeService;
    private final ErpFinanceVoucherDetailService voucherDetailService;

    /**
     * 分页
     * @param bo 查询条件
     * @param pageQuery 分页参数
     * @return
     */
    @Override
    public TableDataInfo<ErpFinanceVoucherVo> pageVouchers(VoucherQueryBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<ErpFinanceVoucher> lqw = new LambdaQueryWrapper<>();
        lqw.like(StrUtil.isNotBlank(bo.getVoucherNo()), ErpFinanceVoucher::getVoucherNo, bo.getVoucherNo())
            .eq(bo.getVoucherTypeId() != null, ErpFinanceVoucher::getVoucherTypeId, bo.getVoucherTypeId())
            .ge(bo.getVoucherDateStart() != null, ErpFinanceVoucher::getVoucherDate, bo.getVoucherDateStart())
            .le(bo.getVoucherDateEnd() != null, ErpFinanceVoucher::getVoucherDate, bo.getVoucherDateEnd())
            .eq(StrUtil.isNotBlank(bo.getPeriod()), ErpFinanceVoucher::getPeriod, bo.getPeriod())
            .eq(bo.getVoucherStatus() != null, ErpFinanceVoucher::getVoucherStatus, bo.getVoucherStatus())
            .eq(bo.getSourceType() != null, ErpFinanceVoucher::getSourceType, bo.getSourceType())
            .like(StrUtil.isNotBlank(bo.getSourceNo()), ErpFinanceVoucher::getSourceNo, bo.getSourceNo())
            .eq(bo.getCreateBy() != null, ErpFinanceVoucher::getCreateBy, bo.getCreateBy())
            .orderByDesc(ErpFinanceVoucher::getVoucherDate)
            .orderByDesc(ErpFinanceVoucher::getVoucherNo);

        IPage<ErpFinanceVoucherVo> result = voucherMapper.selectVoPage(pageQuery.build(), lqw);
        result.getRecords().forEach(t->{
            ErpFinanceVoucherType type = voucherTypeService.getById(t.getVoucherTypeId());
            if(Objects.nonNull(type)){
                t.setTypeName(type.getTypeName());
                //t.setPeriod(type.getPrefix());
            }
        });
        return TableDataInfo.build(result);
    }

    /**
     * 创建记账凭证
     * @param createBo 创建对象
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createVoucher(VoucherCreateBo createBo) {
        // 生成凭证号
        String voucherNo = generateVoucherNo(createBo.getVoucherTypeId(), createBo.getPeriod());

        // 检查凭证号是否存在
        if (checkVoucherNoExists(voucherNo, null)) {
            throw new ServiceException("凭证号已存在");
        }

        ErpFinanceVoucher entity = new ErpFinanceVoucher();
        BeanUtil.copyProperties(createBo, entity);
        entity.setVoucherNo(voucherNo);
        entity.setSerialNumber(voucherNo.substring(voucherNo.length() - 4));
        entity.setTenantId(LoginHelper.getTenantId());
        entity.setCreateDept(LoginHelper.getDeptId());

        //增加分录明细
        List<VoucherDetailCreateBo> detailCreateBoList = createBo.getDetailCreateBoList();
        detailCreateBoList.forEach(d->{
            voucherDetailService.createVoucherDetail(d);
        });

        if (save(entity)) {
            log.info("创建记账凭证成功，ID: {}, 凭证号: {}", entity.getId(), voucherNo);
            return entity.getId();
        }

        throw new ServiceException("创建记账凭证失败");
    }

    /**
     * 更新记账凭证
     * @param updateBo 更新对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateVoucher(VoucherUpdateBo updateBo) {
        ErpFinanceVoucher existEntity = getById(updateBo.getId());
        if (existEntity == null) {
            throw new ServiceException("记账凭证不存在");
        }
        // 检查凭证状态，只有制单状态才能修改
        if (existEntity.getVoucherStatus() != 1) {
            throw new ServiceException("只有制单状态的凭证才能修改");
        }
        BeanUtil.copyProperties(updateBo, existEntity, "id", "voucherNo", "totalDebit", "totalCredit");
        //更新分录
        List<VoucherDetailUpdateBo> detailUpdateBoList = updateBo.getDetailUpdateBoList();
        detailUpdateBoList.forEach(d->{
            voucherDetailService.updateVoucherDetail(d);
        });
        //更新
        if (!updateById(existEntity)) {
            throw new ServiceException("更新记账凭证失败");
        }
        log.info("更新记账凭证成功，ID: {}", updateBo.getId());
    }

    /**
     * 删除记账凭证
     * @param id 凭证ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteVoucher(Long id) {
        ErpFinanceVoucher entity = getById(id);
        if (entity == null) {
            throw new ServiceException("记账凭证不存在");
        }
        // 检查凭证状态，只有制单状态才能删除
        if (entity.getVoucherStatus() != 1) {
            throw new ServiceException("只有制单状态的凭证才能删除");
        }

        // TODO: 检查是否有关联的凭证分录，如果有则不允许删除
        List<ErpFinanceVoucherDetailVo> voucherDetailVos = voucherDetailService.getDetailsByVoucherId(id);
        if(Objects.nonNull(voucherDetailVos)){
            throw new ServiceException("存在关联的凭证分录，则不允许删除");
        }

        if (!removeById(id)) {
            throw new ServiceException("删除记账凭证失败");
        }

        log.info("删除记账凭证成功，ID: {}", id);
    }

    /**
     * 批量删除记账凭证
     * @param ids 凭证ID列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDeleteVouchers(List<Long> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return;
        }

        for (Long id : ids) {
            deleteVoucher(id);
        }
    }

    /**
     * 根据ID获取凭证详情
     * @param id 凭证ID
     * @return 凭证详情
     */
    @Override
    public ErpFinanceVoucherVo getVoucherById(Long id) {
        ErpFinanceVoucher entity = getById(id);
        if (entity == null) {
            throw new ServiceException("记账凭证不存在");
        }
        ErpFinanceVoucherVo vo = convertToVO(entity);
        List<ErpFinanceVoucherDetailVo> detailsByVoucherId = voucherDetailService.getDetailsByVoucherId(vo.getId());
        vo.setDetailVoList(detailsByVoucherId);
        return vo;
    }

    /**
     * 审核凭证
     * @param id 凭证ID
     * @param reviewComment 审核意见
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reviewVoucher(Long id, String reviewComment) {
        ErpFinanceVoucher entity = getById(id);
        if (entity == null) {
            throw new ServiceException("记账凭证不存在");
        }

        if (entity.getVoucherStatus() != 1) {
            throw new ServiceException("只有制单状态的凭证才能审核");
        }

        // 验证借贷平衡
        if (!validateBalance(id)) {
            throw new ServiceException("凭证借贷不平衡，无法审核");
        }

        entity.setVoucherStatus(2);
        entity.setReviewerId(LoginHelper.getUserId());

        if (!updateById(entity)) {
            throw new ServiceException("审核凭证失败");
        }

        log.info("审核凭证成功，ID: {}, 审核员: {}", id, LoginHelper.getUserId());
    }

    /**
     * 反审核凭证
     * @param id 凭证ID
     * @param reason 反审核原因
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unreviewVoucher(Long id, String reason) {
        ErpFinanceVoucher entity = getById(id);
        if (entity == null) {
            throw new ServiceException("记账凭证不存在");
        }

        if (entity.getVoucherStatus() != 2) {
            throw new ServiceException("只有审核状态的凭证才能反审核");
        }

        entity.setVoucherStatus(1);
        entity.setReviewerId(null);

        if (!updateById(entity)) {
            throw new ServiceException("反审核凭证失败");
        }

        log.info("反审核凭证成功，ID: {}, 操作员: {}, 原因: {}", id, LoginHelper.getUserId(), reason);
    }

    /**
     * 记账
     * @param id 凭证ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void postVoucher(Long id) {
        ErpFinanceVoucher entity = getById(id);
        if (entity == null) {
            throw new ServiceException("记账凭证不存在");
        }

        if (entity.getVoucherStatus() != 2) {
            throw new ServiceException("只有审核状态的凭证才能记账");
        }

        entity.setVoucherStatus(3);
        entity.setAccountantId(LoginHelper.getUserId());

        if (!updateById(entity)) {
            throw new ServiceException("记账失败");
        }

        // TODO: 更新总账和明细账

        log.info("记账成功，ID: {}, 记账员: {}", id, LoginHelper.getUserId());
    }


    /**
     * 反记账
     * @param id 凭证ID
     * @param reason 反记账原因
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unpostVoucher(Long id, String reason) {
        ErpFinanceVoucher entity = getById(id);
        if (entity == null) {
            throw new ServiceException("记账凭证不存在");
        }

        if (entity.getVoucherStatus() != 3) {
            throw new ServiceException("只有记账状态的凭证才能反记账");
        }

        entity.setVoucherStatus(2);
        entity.setAccountantId(null);

        if (!updateById(entity)) {
            throw new ServiceException("反记账失败");
        }

        // TODO: 冲销总账和明细账

        log.info("反记账成功，ID: {}, 操作员: {}, 原因: {}", id, LoginHelper.getUserId(), reason);
    }

    /**
     * 结账
     * @param ids 凭证ID列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closeVouchers(List<Long> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return;
        }

        int count = voucherMapper.batchUpdateStatus(ids, 4, LoginHelper.getUserId());
        if (count != ids.size()) {
            throw new ServiceException("结账失败，部分凭证状态不正确");
        }

        log.info("批量结账成功，数量: {}", count);
    }

    /**
     * 反结账
     * @param ids 凭证ID列表
     * @param reason 反结账原因
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void uncloseVouchers(List<Long> ids, String reason) {
        if (CollectionUtil.isEmpty(ids)) {
            return;
        }

        int count = voucherMapper.batchUpdateStatus(ids, 3, LoginHelper.getUserId());
        if (count != ids.size()) {
            throw new ServiceException("反结账失败，部分凭证状态不正确");
        }

        log.info("批量反结账成功，数量: {}, 原因: {}", count, reason);
    }

    /**
     * 批量审核凭证
     * @param ids 凭证ID列表
     * @param reviewComment 审核意见
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchReviewVouchers(List<Long> ids, String reviewComment) {
        if (CollectionUtil.isEmpty(ids)) {
            return;
        }

        for (Long id : ids) {
            reviewVoucher(id, reviewComment);
        }
    }

    /**
     * 批量记账
     * @param ids 凭证ID列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchPostVouchers(List<Long> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return;
        }

        for (Long id : ids) {
            postVoucher(id);
        }
    }

    /**
     * 检查凭证号是否存在
     * @param voucherNo 凭证号
     * @param excludeId 排除的凭证ID
     * @return 是否存在
     */
    @Override
    public boolean checkVoucherNoExists(String voucherNo, Long excludeId) {
        Long count = voucherMapper.checkVoucherNoExists(voucherNo, excludeId);
        return count > 0;
    }

    /**
     * 生成凭证号
     * @param voucherTypeId 凭证类别ID
     * @param period 期间
     * @return 凭证号
     */
    @Override
    public String generateVoucherNo(Long voucherTypeId, String period) {
        return voucherTypeService.generateVoucherNo(voucherTypeId, period);
    }

    /**
     * 获取下一个凭证号
     * @param voucherTypeId 凭证类别ID
     * @param voucherDate 凭证日期
     * @return 凭证号
     */
    @Override
    public String getNextVoucherNo(Long voucherTypeId, LocalDate voucherDate) {
        String period = voucherDate.format(DateTimeFormatter.ofPattern("yyyy-MM"));
        Integer maxSeq = voucherMapper.getMaxSequenceByTypeAndPeriod(voucherTypeId, period);
        int nextSeq = (maxSeq == null ? 0 : maxSeq) + 1;

        // 获取凭证类别信息
        var voucherType = voucherTypeService.getVoucherTypeById(voucherTypeId);
        String prefix = voucherType.getPrefix();
        int numberLength = voucherType.getNumberLength();
        // 格式化序号
        String seqStr = String.format("%0" + numberLength + "d", nextSeq);
        return prefix + "-" + period + "-" + seqStr;
    }

    /**
     * 根据期间获取凭证列表
     * @param period 期间
     * @return 凭证列表
     */
    @Override
    public List<ErpFinanceVoucherVo> getVouchersByPeriod(String period) {
        return voucherMapper.selectVouchersByPeriod(period);
    }

    /**
     * 获取期间内凭证状态统计
     * @param period 期间
     * @return 统计结果
     */
    @Override
    public List<VoucherStatusCount> getVoucherStatusCount(String period) {
        return voucherMapper.getVoucherStatusCountByPeriod(period);
    }

    /**
     * 根据来源单据查询凭证
     * @param sourceId 来源单据ID
     * @param sourceNo 来源单据号
     * @return 凭证列表
     */
    @Override
    public List<ErpFinanceVoucherVo> getVouchersBySource(Long sourceId, String sourceNo) {
        return voucherMapper.selectVouchersBySource(sourceId, sourceNo);
    }

    /**
     * 复制凭证
     * @param id 源凭证ID
     * @param newVoucherDate 新凭证日期
     * @return 新凭证ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long copyVoucher(Long id, LocalDate newVoucherDate) {
        ErpFinanceVoucher sourceVoucher = getById(id);
        if (sourceVoucher == null) {
            throw new ServiceException("源凭证不存在");
        }

        // 解析源凭证号，获取主凭证标识和当前子序号
        String[] parsedResult = voucherTypeService.parseSourceVoucherNo(sourceVoucher.getVoucherNo());
        String mainVoucherNo = parsedResult[0]; // 主凭证标识（如"记-2025-08-0001"）
        String currentSubNo = parsedResult[1];  // 源凭证的子序号（可能为null）

        // 查询当前主凭证下的最大子序号，计算新子序号
        int newSubSerial = voucherTypeService.calculateNewSubSerial(mainVoucherNo);

        // 生成新凭证号（主凭证号+子序号，如"记-2025-08-0001-001"）
        String newVoucherNo = mainVoucherNo + "-" + String.format("%03d", newSubSerial);

        // 复制源凭证属性并设置新值
        ErpFinanceVoucher newVoucher = new ErpFinanceVoucher();
        BeanUtil.copyProperties(sourceVoucher, newVoucher, "id", "voucherNo", "createTime", "updateTime", "createBy", "updateBy");
        newVoucher.setVoucherDate(newVoucherDate);
        newVoucher.setPeriod(newVoucherDate.format(DateTimeFormatter.ofPattern("yyyy-MM")));
        newVoucher.setVoucherStatus(1); // 制单状态
        newVoucher.setVoucherNo(newVoucherNo);
        newVoucher.setTenantId(LoginHelper.getTenantId());
        newVoucher.setCreateDept(LoginHelper.getDeptId());

        if (!save(newVoucher)) {
            throw new ServiceException("复制凭证失败");
        }

        // TODO: 复制源凭证的分录信息到新凭证
        copyVoucherItems(sourceVoucher, newVoucher);

        log.info("复制凭证成功，源凭证ID: {}, 新凭证ID: {}", id, newVoucher.getId());
        return newVoucher.getId();
    }

    /**
     * 复制源凭证的分录信息到新凭证
     * @param sourceVoucher
     * @param newVoucher
     */
    private void copyVoucherItems(ErpFinanceVoucher sourceVoucher, ErpFinanceVoucher newVoucher) {
        List<ErpFinanceVoucherDetailVo> details = voucherDetailService.getDetailsByVoucherId(sourceVoucher.getId());
        details.forEach(d->{
            voucherDetailService.copyVoucherDetails(d.getVoucherId(),newVoucher.getId());
        });
    }

    /**
     * 获取用户待审核凭证数量
     * @param userId 用户ID
     * @return 数量
     */
    @Override
    public Long getPendingReviewCount(Long userId) {
        return voucherMapper.getPendingReviewCount(userId);
    }

    /**
     * 验证凭证借贷平衡
     * @param id 凭证ID
     * @return 是否平衡
     */
    @Override
    public boolean validateBalance(Long id) {
        ErpFinanceVoucher voucher = getById(id);
        if (voucher == null) {
            return false;
        }

        // 检查借贷平衡
        return voucher.getTotalDebit().compareTo(voucher.getTotalCredit()) == 0
            && voucher.getTotalDebit().compareTo(BigDecimal.ZERO) > 0;
    }

    /**
     * 更新凭证金额合计
     * @param id 凭证ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateVoucherTotals(Long id) {
        // 关联凭证分录表进行计算
        List<ErpFinanceVoucherDetailVo> voucherDetailVos = voucherDetailService.getDetailsByVoucherId(id);
        BigDecimal totalDebit = BigDecimal.ZERO;
        BigDecimal totalCredit = BigDecimal.ZERO;

        // 累加分录金额
        for (ErpFinanceVoucherDetailVo detail : voucherDetailVos) {
            BigDecimal debit = detail.getDebitAmount() != null ? detail.getDebitAmount() : BigDecimal.ZERO;
            BigDecimal credit = detail.getCreditAmount() != null ? detail.getCreditAmount() : BigDecimal.ZERO;
            totalDebit = totalDebit.add(debit);
            totalCredit = totalCredit.add(credit);
        }

        ErpFinanceVoucher erpFinanceVoucher = this.baseMapper.selectById(id);
        if (erpFinanceVoucher == null) {
            throw new ServiceException("凭证不存在，ID: " + id);
        }

        erpFinanceVoucher.setTotalDebit(totalDebit);
        erpFinanceVoucher.setTotalCredit(totalCredit);
        this.updateById(erpFinanceVoucher);
        log.info("更新凭证金额合计，ID: {}", id);
    }

    /**
     * 检查期间是否已结账
     * @param period 期间
     * @return 是否已结账
     */
    @Override
    public boolean isPeriodClosed(String period) {
        // TODO: 检查期间是否已结账
        // 这里需要关联期间结账表进行检查
        return false;
    }

    /**
     * 获取凭证打印数据
     * @param id 凭证ID
     * @return 打印数据
     */
    @Override
    public ErpFinanceVoucherVo getVoucherForPrint(Long id) {
        ErpFinanceVoucherVo vo = getVoucherById(id);
        // TODO: 加载打印需要的额外数据，如凭证分录等
        return vo;
    }

    /**
     * 转换为VO对象
     */
    private ErpFinanceVoucherVo convertToVO(ErpFinanceVoucher entity) {
        ErpFinanceVoucherVo vo = new ErpFinanceVoucherVo();
        BeanUtil.copyProperties(entity, vo);

        // 设置状态名称
        vo.setVoucherStatusName(getVoucherStatusName(entity.getVoucherStatus()));
        vo.setSourceTypeName(getSourceTypeName(entity.getSourceType()));
        return vo;
    }

    /**
     * 获取凭证状态名称
     */
    private String getVoucherStatusName(Integer status) {
        if (status == null) return "";
        switch (status) {
            case 1: return "制单";
            case 2: return "审核";
            case 3: return "记账";
            case 4: return "结账";
            default: return "未知";
        }
    }

    /**
     * 获取来源类型名称
     */
    private String getSourceTypeName(Integer sourceType) {
        if (sourceType == null) return "";
        switch (sourceType) {
            case 1: return "手工录入";
            case 2: return "系统生成";
            default: return "未知";
        }
    }
}
